package com.frame.library.http;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.frame.library.db.DaoSupport;
import com.frame.library.db.DaoSupportFactory;
import com.frame.library.db.IDaoSupport;
import com.google.gson.Gson;
import com.tom.baselibrary.MD5Utils;
import com.tom.baselibrary.http.EngineCallback;
import com.tom.baselibrary.http.HttpUtils;
import com.tom.baselibrary.http.IHttpEngine;
import com.tom.baselibrary.http.SPHttpCache;
import com.tom.baselibrary.http.Utils;

import java.io.File;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * 默认网络引擎 OkHttp
 * 具体的访问实现
 */
public class OkHttpEngine implements IHttpEngine {
    private static final String TAG = "OkHttpEngine";
    private static OkHttpClient mOkHttpClient = new OkHttpClient();

    private SPHttpCache mHttpCache;

    public OkHttpEngine() {
        mHttpCache = new SPHttpCache();
    }

    @Override
    public void get(Context context, String url, Map<String, Object> params,
                    final EngineCallback httpCallBack, final boolean cache) {
        // 省略部分代码......， tag用于取消网络请求。
        // 处理缓存。
        final String finalUrl = Utils.jointParams(url, params);  //请求路径
        //  Log.e(TAG, "finalUrl: " + finalUrl);

        // 写一大堆处理逻辑 ，内存怎么扩展等等
        // ①.判断是否需要缓存，然后判断 是否有缓存
        if (cache) { // 需要缓存
            String resultJson = CacheDataUtil.getCacheResultJson(finalUrl); // 从数据库拿缓存
            if (!TextUtils.isEmpty(resultJson)) {
                Log.e(TAG, "已读取到缓存: " );
                // 1.1 要缓存，且数据库有缓存, 立马直接执行显示数据
                httpCallBack.onSuccess(resultJson);
            }
        }

        Request.Builder requestBuilder = new Request.Builder().url(url).tag(context);
        Request request = requestBuilder.build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "get onFailure: " + e.getMessage());
                HttpUtils.handler.post(new Runnable() {
                    @Override
                    public void run() {
                        httpCallBack.onError(e);
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String resultJson = response.body().string();
                Log.e(TAG, "get onResponse: " + resultJson);
                // 当然有的时候还需要不同的些许处理
                HttpUtils.handler.post(new Runnable() { // 切换到主线程中
                    @Override
                    public void run() {
                        // ②.将获取的数据，先比对上次 的内容
                        if (cache) {
                            // 2.0 拿到缓存数据，用来做比对数据
                            String cacheResultJson = CacheDataUtil.getCacheResultJson(finalUrl);
                            if (resultJson.equals(cacheResultJson)) {
                                // 2.1 内容与缓存一致，不要执行成功方法去刷新界面。
                                Log.e(TAG, "----数据和缓存的一致，不做任何处理---- ");
                                return;
                            }
                        }
                        // 2.2 执行成功方法
                        httpCallBack.onSuccess(resultJson);
                        Log.e(TAG, "get返回结果: " + resultJson);
                        if (cache) {
                            // 2.3 缓存数据到数据库中(先删除，再插入数据)
                            long number = CacheDataUtil.cacheData(finalUrl, resultJson);
                            // Log.e(TAG, "----缓存数据到数据库中---- " +number);
                        }
                    }
                });
            }
        });
    }

    @Override
    public void post(Context context, String url, Map<String, Object> params,
                     EngineCallback callback, final boolean cache) {
        String joinUrl = HttpUtils.joinParams(url, params); // 用来调试打印。
        Log.e(TAG, "post: " + joinUrl);

        // 省略部分代码......
        RequestBody requestBody = appendBody(params);
        Request request = new Request.Builder()
                .url(url)
                .tag(context)
                .post(requestBody)
                .build();

        mOkHttpClient.newCall(request).enqueue(
                new Callback() {
                    @Override
                    public void onFailure(Call call, final IOException e) {
                        // 需要 在主线程中统一处理，处理回调
                        executeError(callback, e); // 执行自己的回调callback
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        // onResponse、onFailure 不是在主线程中。
                        String resultJson = response.body().string();
                        Log.e(TAG, "post onResponse: " + resultJson);
                        executeSuccessMethod(callback, resultJson);
                        // TODO: 缓存处理，下一期我们没事干，自己手写数据库框架
                    }
                }
        );
    }

    /**
     * 执行成功的方法
     **/
    private void executeSuccessMethod(final EngineCallback httpCallBack, final String resultJson) {
        try {
            HttpUtils.handler.post(new Runnable() {
                @Override
                public void run() {
                    httpCallBack.onSuccess(resultJson);
                }
            });
        } catch (Exception e) {
            executeError(httpCallBack, e);
            e.printStackTrace();
        }
    }

    /**
     * 执行失败的方法
     */
    private void executeError(final EngineCallback httpCallBack, final Exception e) {
        HttpUtils.handler.post(new Runnable() {
            @Override
            public void run() {
                httpCallBack.onError(e);
            }
        });
    }


    /**
     * 组装post请求的参数
     */
    protected RequestBody appendBody(Map<String, Object> params) {
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        addParams(builder, params);
        return builder.build();
    }

    // 添加参数
    private void addParams(MultipartBody.Builder builder, Map<String, Object> params) {
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                builder.addFormDataPart(key, params.get(key) + "");
                Object value = params.get(key);
                if (value instanceof File) { // 处理文件--》 object，File
                    File file = (File) value;
                    builder.addFormDataPart(key, file.getName(),
                            RequestBody.create(MediaType.parse(guessMineType(file.getAbsolutePath())), file));
                } else if (value instanceof List) { // 提交list集合【文件的list集合】
                    try {
                        List<File> listFile = (List<File>) value;
                        for (int i = 0; i < listFile.size(); i++) {
                            // 拿到集合中的文件
                            File file = listFile.get(i);
                            builder.addFormDataPart(key + i, file.getName(),
                                    RequestBody.create(MediaType.parse(guessMineType(file.getAbsolutePath())), file));
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    builder.addFormDataPart(key, value + "");
                }
            }
        }
    }

    /**
     * 猜测文件类型，用于文件上传
     */
    private String guessMineType(String path) {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        String contentTypeFor = fileNameMap.getContentTypeFor(path);
        if (contentTypeFor == null) {
            contentTypeFor = "application/octet-stream";
        }
        return contentTypeFor;
    }

}

// 问题优化：
// 如何缓存到内存卡？数据库。第三方数据都是在 data/data/***/database
// 回调的 json-String转Bean？ 但是不能直接用泛型。
// 每次参数请求太多。公共参数处理

